function[out,momentsModel,errorMes,model,Q,dataSim] = objectFunc(paramsInputValues,setup)
% This function evaluates the objective function for GMM estimation of the RBC model
% errorMes = 0, everything is ok, 1 for reporting problems)
% Initializing the outputs
out              = [];
momentsModel     = [];
model            = [];
Q                = NaN;
dataSim          = [];


% Check if the lower and upper bounds are violated
if any(paramsInputValues < setup.lowerBoundsValues) || any(paramsInputValues > setup.upperBoundsValues)
    errorMes = 1;
    if setup.optim == 1 || setup.optim == 3
        out = NaN;
    elseif setup.optim == 2
        out = ones(size(setup.dataMoments,1),1)*1D10;
    end
    return;
end

% We turn paramsInputValues into a struct 
for i=1:size(setup.selectParams,1)
    name = setup.selectParams(i,1);
    paramsInput.(name{1}) = paramsInputValues(i,1);
end

% Accounting for calibrated coefficients, we unold paramsInput by to get params 
params = paramsInput;
% The setdiff implies that we do not overwrite elements in params based on calibrateparams
namesCalibrate = setdiff(fieldnames(setup.calibrateParams),setup.selectParams);
for i=1:size(namesCalibrate,1)
   name = namesCalibrate(i);  
   params.(name{1}) = setup.calibrateParams.(name{1}); 
end

% Solve the model
[model,errorMes] = perturbationDSGEmodel(params,setup.orderApp,setup.setupModel);

if errorMes == 1
    if setup.optim == 1 || setup.optim == 3
        out = NaN;
    elseif setup.optim == 2
        out = ones(size(setup.dataMoments,1),1)*1D10;
    end
    return;
end

% Computing model moments
rng(1,'twister'); 
T = size(setup.data,1);
shocksSim = randn(model.ne,T*setup.tau);
shocksSim = (shocksSim - repmat(mean(shocksSim,2),1,T*setup.tau))./repmat(std(shocksSim,0,2),1,T*setup.tau);
if setup.appMethod == 1
    %ySim = sim3rdPrun(model,shocks,orderApp);
    x0=model.h0;    % initial state is set at the steady state
    pruning=0;      % no pruning
    ySim=simul(x0,shocksSim,model.g0,model.h0,model.eta,model.derivs,setup.orderApp,pruning);
elseif setup.appMethod == 2
    %adding information to setupEPer which depends on params
    setup.setupEPer = infoToSetupEPer(model,setup.setupEPer);    
    ySim = simExtendedPer(model,shocksSim,setup.setupEPer);
end
ySim = untransformYandX(ySim,model);
dataSim = ySim(setup.selectY,:)';
dataSimInfo      = momentsGMMData(dataSim,setup.autoLagsIdx,setup.inclMoms);
momentsModel     = dataSimInfo.dataMoments;

% The moments implied by the model
residuals    = setup.Sw*(setup.dataInfo.dataMoments-momentsModel);
%Q = (dataMoments-momentsModel)'*(Sw'*Sw)*(dataMoments-momentsModel);
Q = residuals'*residuals;
if setup.optim == 1 || setup.optim == 3
    out = residuals'*residuals;
elseif setup.optim == 2
    out = residuals;
end
    

end

